package com.globant.datascience.recommender;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVStrategy;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.impl.model.MemoryIDMigrator;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.TanimotoCoefficientSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

class ShoppingCartRecommender {

	private static DataModel dataModel;
	
	private GenericUserBasedRecommender recommender;
	
	private MemoryIDMigrator thing2long = new MemoryIDMigrator();


	public void initRecommender(String filename) {
		try {
			URL url = getClass().getClassLoader().getResource(filename);

			File data = new File(url.toURI());

			Map<Long,List<Preference>> preferecesOfUsers = new HashMap<Long,List<Preference>>();
			
			CSVParser parser = new CSVParser(new InputStreamReader(new FileInputStream(data), "UTF-8"));
			
			String[] header = parser.getLine();

			String[] line;

			while((line = parser.getLine()) != null) {

				String usuario = line[0];
				String producto = line[1];
				String rating = line[2];

				long userLong = Long.valueOf(usuario);
				thing2long.storeMapping(userLong, usuario);

				long itemLong = thing2long.toLongID(producto);
				thing2long.storeMapping(itemLong, producto);
				
				long ratingLong = thing2long.toLongID(rating);
				thing2long.storeMapping(ratingLong, producto);

				List<Preference> userPrefList;

				if((userPrefList = preferecesOfUsers.get(userLong)) == null) {
					userPrefList = new ArrayList<Preference>();
					preferecesOfUsers.put(userLong, userPrefList);
				}

				// use a rating of 1 for all items
				userPrefList.add(new GenericPreference(userLong, itemLong, ratingLong));
			}

			// create the corresponding mahout data structure from the map
			FastByIDMap<PreferenceArray> preferecesOfUsersFastMap = new FastByIDMap<PreferenceArray>();
			for(Entry<Long, List<Preference>> entry : preferecesOfUsers.entrySet()) {
				preferecesOfUsersFastMap.put(entry.getKey(), new GenericUserPreferenceArray(entry.getValue()));
			}

			// create a data model 
			dataModel = new GenericDataModel(preferecesOfUsersFastMap);

			// Instantiate the recommender
			UserSimilarity similarity = new TanimotoCoefficientSimilarity(dataModel);
			UserNeighborhood neighborhood =
					new NearestNUserNeighborhood(2, similarity, dataModel);

			recommender = new GenericUserBasedRecommender(
					dataModel, neighborhood, similarity);

			
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TasteException e) {
			e.printStackTrace();
		}
	}
	
	public List<RecommendedItem> recommend(Long id,int recommendationSize) throws TasteException {
		try {
			List<RecommendedItem> recommendations = recommender.recommend(id, recommendationSize);
			return recommendations;
		} catch (TasteException e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	public void printRecommendations(List<RecommendedItem> recommendations) {
		for (RecommendedItem recommendation : recommendations) {
			System.out.println(recommendation);
			System.out.println(thing2long.toStringID(recommendation.getItemID()));
		}	
	}
	
	public long[] getSimilarUserIds(Long id, int similarUsers) throws TasteException {
		try {
			long[] users = recommender.mostSimilarUserIDs(id,similarUsers);
			return users;
		} catch (TasteException e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	public void printSimilarUserIds(Long id,long[] users) {
		try {
			for (int i= 0; i< users.length ; i++) {
				System.out.println("similar user id = " + users[i] );
				System.out.println(recommender.getSimilarity().userSimilarity(id,  users[i] ));
			}
		} catch (TasteException e) {
			e.printStackTrace();
		}
	}

}
